package cn.itcast.netty.c3;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.Scanner;


/**
 * 客户端可以一直源源不断的发送数据给服务器端
 * 如果客户端哪天不想发送了 输入一个Q就可以中断客户端
 */
@Slf4j
public class CloseFutureClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group =  new NioEventLoopGroup();
        ChannelFuture channelFuture = new Bootstrap()
                .group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));  //这个是netty自带的方便我们调试的一个处理器 设置日志级别为debug
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8080));
        channelFuture.sync();
        Channel channel = channelFuture.channel();
        new Thread(()->{
            Scanner sc = new Scanner(System.in);
            while (true){
                String line = sc.nextLine();
                if("q".equals(line)){
                    channel.close();
                    //log.debug("处理关闭之后的操作"); 放在这里也是不行的因为channel.close()本身也是异步方法所以也是由另外的线程来把数据通知给这个线程的
                    break;
                }
                channel.writeAndFlush(line);
            }
        },"input").start();

        //其实这行代码放在这里是错误的,因为我们新建了一个新的线程去处理发送数据跟关闭操作,但是这里是主线程执行的
        //log.debug("处理关闭之后的操作");

        //使用closeFuture对象来处理  两种方式  同步处理关闭 / 异步处理关闭
        ChannelFuture closeFuture = channel.closeFuture();
        //同步处理
//        closeFuture.sync(); //这个方法是阻塞的，只有当执行了channel.close()之后，代码才会继续往下执行 专门处理上面说的channel.close是异步执行的问题
        //所以在这下面处理关闭之后的操作的是可以的
//        System.out.println("waiting close ...");
//        log.debug("处理关闭之后的操作");

        //异步处理方案,做一个监听器关闭,其实就是当接收到了close关闭请求后 让监听器去做处理关闭 当调用channel.close的时候就会走到这里面的这个方法来
        closeFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                log.debug("处理关闭之后的操作");
                //还有个问题，我们客户端写了q退出了看代码所有线程也跑完了 但是看控制台为什么进程没有关不还得我们自己在控制台点击那个正方形的红色按钮去关闭呢？
                //因为NioEventLoopGroup这个组里面还有其他的线程在运行，所以我们得优雅一点把他们全关闭了，那么这个进程就也会自己关闭了

                //这就有点像linux中的 kill命令 把当前还在连接的请求处理完 然后再关闭  kill-0就是立刻现在马上杀掉的意思
                //在异常发生的时候也别忘了要杀掉这些线程让进程结束
                group.shutdownGracefully();

            }
        });


    }
}
